﻿using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Linq.PageList;
using Dyao.Book.Contract;
using Foundation.Core;

namespace Dyao.Book.Core
{
    public static class PaginationExtensions
    {
        private static ConcurrentDictionary<Type, IDictionary<string, Action<object, bool>>> _orderModelSetter = new ConcurrentDictionary<Type, IDictionary<string, Action<object, bool>>>();

        public static TOrderBy GetOrderBy<TOrderBy>(this PaginationFilterDto paginationModel)
          where TOrderBy : new()
        {
            var type = typeof(TOrderBy);
            var orderBy = new TOrderBy();

            if (!paginationModel.SortName.IsNullOrWhiteSpace())
            {
                _orderModelSetter.GetOrAdd(type, t =>
                {
                    var properties = type.GetProperties().Where(p => p.PropertyType == typeof(bool?) || p.PropertyType == typeof(bool));
                    var dict = new Dictionary<string, Action<object, bool>>(StringComparer.OrdinalIgnoreCase);
                    var paramerter = Expression.Parameter(typeof(object), "m");
                    var value = Expression.Parameter(typeof(bool), "value");
                    var convert = Expression.Convert(paramerter, type);

                    foreach (var property in properties)
                    {
                        Expression pValue = value;
                        if (property.PropertyType.IsNullableType())
                        {
                            pValue = Expression.Convert(value, typeof(bool?));
                        }

                        var action = Expression.Lambda<Action<object, bool>>(Expression.Call(convert, property.SetMethod, pValue), paramerter, value).Compile();

                        dict.Add(property.Name, action);
                    }


                    return dict;
                }).TryGetValue(paginationModel.SortName, out var setter);

                if (setter != null)
                {
                    setter(orderBy, string.IsNullOrEmpty(paginationModel.SortOrder) || string.Equals(paginationModel.SortOrder, "asc", StringComparison.OrdinalIgnoreCase));
                }
            }

            return orderBy;
        }

        public static IPageFilter<TFilter> ToPageFilter<TFilter>(this TFilter filter)
            where TFilter : PaginationFilterDto, new()
        {
            if (filter == null)
            {
                filter = new TFilter();
            }

            if (filter.PageIndex < 0)
            {
                filter.PageIndex = Defaults.GridPage;
            }
            if (filter.PageSize <= 0)
            {
                filter.PageSize = Defaults.GridPageSize;
            }

            return new PageFilter<TFilter>(filter, filter.PageIndex, filter.PageSize) { IsQueryRecords = filter.IsQueryRecords };
        }

        public static IPageFilter<TFilter, TOrderBy> ToPageFilter<TFilter, TOrderBy>(this TFilter filter)
            where TFilter : PaginationFilterDto, new()
            where TOrderBy : new()
        {
            var order = filter.GetOrderBy<TOrderBy>();

            return filter.ToPageFilter(order);
        }

        public static IPageFilter<TFilter, TOrderBy> ToPageFilter<TFilter, TOrderBy>(this TFilter filter, TOrderBy orderBy)
            where TFilter : PaginationFilterDto, new()
        {
            if (filter == null)
            {
                filter = new TFilter();
            }

            if (filter.PageIndex < 0)
            {
                filter.PageIndex = Defaults.GridPage;
            }
            if (filter.PageSize <= 0)
            {
                filter.PageSize = Defaults.GridPageSize;
            }

            return new PageFilter<TFilter, TOrderBy>(filter, orderBy, filter.PageIndex, filter.PageSize) { IsQueryRecords = filter.IsQueryRecords };
        }

        public static IPageFilter<TFilter, TOrderBy> ToPageFilter<TPagination, TFilter, TOrderBy>(this TPagination pagination, TOrderBy orderBy, TFilter filter)
            where TPagination : PaginationFilterDto, new()
        {
            if (pagination == null)
            {
                pagination = new TPagination();
            }

            if (pagination.PageIndex < 0)
            {
                pagination.PageIndex = Defaults.GridPage;
            }
            if (pagination.PageSize <= 0)
            {
                pagination.PageSize = Defaults.GridPageSize;
            }

            var pageList = new PageFilter<TFilter, TOrderBy>(filter, orderBy, pagination.PageIndex, pagination.PageSize) { IsQueryRecords = pagination.IsQueryRecords };
            return pageList;
        }

        public static async Task<IPageFilter<TFilter, TOrderBy>> ToPageFilterAsync<TPagination, TFilter, TOrderBy>(this TPagination pagination, TOrderBy orderBy, Func<TPagination, Task<TFilter>> filterFunc)
            where TPagination : PaginationFilterDto, new()
        {
            if (pagination == null)
            {
                pagination = new TPagination();
            }

            if (pagination.PageIndex < 0)
            {
                pagination.PageIndex = Defaults.GridPage;
            }
            if (pagination.PageSize <= 0)
            {
                pagination.PageSize = Defaults.GridPageSize;
            }

            var filter = await filterFunc(pagination);
            return new PageFilter<TFilter, TOrderBy>(filter, orderBy, pagination.PageIndex, pagination.PageSize) { IsQueryRecords = pagination.IsQueryRecords };
        }
    }
}
